
#include "Monster.h"

Monster::Monster(Point& location)
	:Movables(location, RADIUS_OF_MOVABLE, 1, MONSTER, getDefaultBitmap(MONSTER_BITMAP_NORMAL)),
	_is_ghost(false),
	_blow_up_size(1),
	_max_blow_up_size(10),
	_ghost_mode_counter(0)
{
	//ensure that the data vectors are empty
	_monster_route.clear();
	_direction_list.clear();
}

DetectionResult Monster::collisionCheck(Movable_type position_type) const
{
	switch(position_type)
	{
	case HOLE:
		return CAN_MOVE;
		break;
	case SAND:
		return COLLISION;
		break;
	case DIGDUG:
		return DIGDUG_DEAD;
		break;
	case MONSTER:
		return CAN_MOVE;
		break;
	case PROJECTILE:
		return CAN_MOVE;
		break;

	}

	return DIGDUG_DEAD;	//Should never reach here!
}


DetectionResult Monster::move(Map& map)
{
	//! _direction for monster to move is updated in Game class
	DetectionResult result = COLLISION;

	if (_is_ghost == false && _blow_up_size < 2)
	{
		
		result  = Movables::checkMoveInDirection(_location, _direction,  map); //! Make use of DRY principle
	
		if(result == CAN_MOVE)
		{
			int delta_x = delta(_direction).at(0);
			int delta_y = delta(_direction).at(1);
			_location = Point(_location.x() + delta_x, _location.y() + delta_y);
		}
	}
	else if(_blow_up_size < 2)
	{
		moveGhost();
		//if(getValidDirections(_location, map).size() > 2)
			_is_ghost = false;
	}

	return result;
}

bool Monster::blowUp()
{
	_blow_up_size++;
	if(_blow_up_size == _max_blow_up_size)
		return true;
	else return false;
}

void Monster::allowMove(Direction direction)
{
	_blow_up_size = 1;
	_direction = direction;
}

Point Monster::nextCoordinate()
{
	Point co_ordinate(_location.x(), _location.y());	//if monster has no route to follow then return current location

	int monster_route_size = _monster_route.size();
	if(monster_route_size != 0)
	{
		//obtain the co_ordinate from the next position along the route which the monster should follow
		_monster_route.pop_back();	//remove previous move position
		co_ordinate = _monster_route.back();			
		
	}
	
	return co_ordinate;
}

void Monster::updateMoveDirection(Point& digdug_location, Map& map)
{
	Point next_co_ord = nextCoordinate();

	if(next_co_ord == _location)	//If monster route is empty, ie if he cannot reach digdug then self_route monster
	{
		_direction = selfRoute(digdug_location, map);		
	}
	else
	{
		if(next_co_ord.y() < _location.y()) _direction = UP;
		else if(next_co_ord.y() > _location.y()) _direction = DOWN;
		else if(next_co_ord.x() < _location.x()) _direction = LEFT;
		else _direction = RIGHT;

	}
}
Direction Monster::selfRoute(Point& digdug_location, Map& map)
{
		//Know where we have come from and what our prevous options were
	 Direction previous_direction = _direction;
	 vector <Direction> previous_direction_list = _direction_list;
	 vector <Direction> new_direction_list = getValidDirections(_location, map);
	
	 //If things have changed since our last move....
	 //Then try and work out a new direction to go in.

	bool reevaluate = true;
	if(new_direction_list.size() == previous_direction_list.size() + 1 && previous_direction_list.size() != 1)
		reevaluate = false;

	 if(new_direction_list != previous_direction_list && reevaluate == true)
	 {
		 
		 _direction_list = new_direction_list;

		 Direction desired_x_direction = RIGHT;
		 Direction desired_y_direction = DOWN; 
		 Direction desired_direction; 

		 int delta_x = _location.x() - digdug_location.x();
		 int delta_y =  _location.y() - digdug_location.y();

		 if(delta_x > 0)
			 desired_x_direction = LEFT;
		 if(delta_y > 0)
			 desired_y_direction = UP;
		 
		 if(new_direction_list.size() == 1)
		 {
			 return new_direction_list.at(0);
		 }

		 if(abs(delta_x) > abs(delta_y) && contains(new_direction_list, desired_x_direction))
		 {
			 desired_direction = desired_x_direction;
			 return desired_direction;
		 }
		 else if (contains(new_direction_list, desired_y_direction))
		 {
			 desired_direction = desired_y_direction; 
			 return desired_direction;
		 }
		 else if(contains(new_direction_list, previous_direction)) 
		 {
			 return previous_direction;
		 }

	 }
	 else
	 {
		 return previous_direction;
	 }

}

int Monster::blowUpSize()
{
	return _blow_up_size;
}

//Returns a vector contianing all of the valid moves at that moment. 
vector <Direction> Monster::getValidDirections(Point& location, Map& map)
{

	vector <Direction> directions;
	Direction direction;

	int x = location.x();
	int y = location.y();
	int length = 2*RADIUS_OF_MOVABLE + 1;


	for(int i = 0; i < 4; i++)
	{
		direction = static_cast<Direction>(i);

		DetectionResult result = checkMoveInDirection(location, direction, map);

		if (result != COLLISION)
		{
			directions.push_back(direction);
		}
	
	}

	return directions;
}


void Monster::moveGhost()
{
	int x_pos = _location.x();
	int y_pos = _location.y();

	int delta_x = _ghost_end_position.x() - x_pos; 
	int delta_y = _ghost_end_position.y() - y_pos;

	if (delta_x < 0)
	{
		_location = Point(x_pos - 1, y_pos); //move monster left
	}
	else if (delta_x > 0) 
	{
		_location = Point(x_pos + 1, y_pos); //move monster right
	}

	if (delta_y < 0)
	{
		_location = Point(x_pos, y_pos - 1); //move monster up
	}
	else if (delta_y > 0) 
	{
		_location = Point(x_pos, y_pos + 1); //move monster down
	}

	if (delta_x == 0 && delta_y == 0)
	{
		_is_ghost = false;
	}
}

void Monster::updateGhostMode(Point& digdug_location)
{
	if(_is_ghost == false && _monster_route.empty())
	{
		_ghost_mode_counter++;
	}
	else if(!_monster_route.empty())
	{
		_ghost_mode_counter = 0;
	}

	//!The ghost mode counter is incremented every move if the monster cannot reach digdug
	if (_is_ghost == false && _ghost_mode_counter == 5 && _blow_up_size <2)	//! Set ghost mode if the ghost mode counter reaches a given value
	{
		_ghost_mode_counter = 0;
		_ghost_end_position = digdug_location;
		_is_ghost = true;
	}
}

bool Monster::isGhost()
{
	return _is_ghost;
}

void Monster::setAutoRoute(vector<Point> route)
{
	_monster_route = route;
}



void Monster::clearRoute()
{
	_monster_route.clear();
}


void routeMonsters(Map &map, Point &digdug_position, vector <Monster> & monsters )
{
	//Exit the function of there are no monsters!
	if(monsters.size() ==0) return;	//!Return if there are no monsters
	int monsters_found = 0;

	for(int i = 0; i < monsters.size(); i++)
	{
		monsters.at(i).clearRoute();
	}

	//Variables
	 vector <vector <Point>> routes;	//routes Stores points as they are generated
	 vector <Point>	 point_monitor; //!point_monitor Stores a list of points that have been routed to, and the moves to that point
	 vector <Direction> directions; //!A list of directions to move in

	 //Clear Vectors
	 routes.clear();
	 point_monitor.clear();
	 directions.clear();

	//Step 1: Get the posision of Digdug. 
	//Note: This is the start Position for our routing, so put it into our list;
	routes.push_back(vector <Point>(0));
	routes.at(0).push_back(digdug_position);
	point_monitor.push_back(digdug_position);
	
	//Step 2: Enter into the routing loop
	//Note: This runs until all the monsters have been routed. 
	do
	 {
		//Step 3: Enter into the routes loop. 
		//Note: Starts at the first PointsList and goes until the last
		 int k = 0; 
		 int number_of_new_routes_added = 0;
		 do
		 {
			//Step 4: Looking at the last position on a specific PointsList check what directions are valid
			 Point this_point = routes.at(k).back();
			 directions = monsters.at(0).getValidDirections(this_point, map);//!<NB: Use the getValidDirections of any monster!
			
			 bool pushed_onto_already = false; //You only want to push one point onto the current route
			 bool route_was_deleted = false;   //Must know if point was deleted, as this will shift everything down
			 bool added_to_the_list = false;   //Want to know if a new point was added to the list
			
			 //Step 5: Consider the available directions, these yield new points
			//Note: If a given point is already contained within our point_monitor, then dont add it
			 for(int i = 0; i < directions.size(); i++)
			 {
				 int x = this_point.x() + delta(directions.at(i)).at(0);	//The new X
				 int y = this_point.y() + delta(directions.at(i)).at(1);	//The new Y
				 Point new_point(x,y);

				 bool point_is_repeated = contains(point_monitor, new_point);

				 //Step 6: Add the new point to route being analysed.
				 //For the first new point, simply push it onto the current PointList  
				 if(!point_is_repeated)
				 {
					 added_to_the_list = true;
					 if(!pushed_onto_already)
					 {
						 pushed_onto_already = true; 
						 routes.at(k).push_back(new_point);	//Add the point onto our route
						 monsters_found+= monsterLocatedcheck(routes.at(k), monsters);
					 }
					//All other new elements must go onto their own new Pointlist at the back.
					 else 
					 {
						vector <Point> temp_route =  routes.at(k);
						temp_route.pop_back();
						routes.push_back(temp_route);			//Clone the PointList
						routes.back().push_back(new_point);		//Add the point onto our route
						monsters_found+=monsterLocatedcheck(routes.back(), monsters);
						number_of_new_routes_added++;
					 } 

					 point_monitor.push_back(new_point);		//Add the point to our monitor list
					
				 }
				
			 }
			 
			 //Step 7: If No new points were added to the list, it means that we hit a dead end of sorts
			 //Either we have visited this place before, or there is no option but back.
			if(added_to_the_list == false)
			{
				vector<vector <Point>>::const_iterator temp_iter = routes.begin() +k;
				//Gain access to the current element
				routes.erase(temp_iter);				//Delete that route from the list
			}
			else
			{
				k++;
			}
			for(int l = 0; l < routes.size(); l++)
			{
				if(routes.at(l).size() > 25)
				{
					break;
				}
			}
			cout<<" There are "<<routes.size()<<" routes"<<endl;
		 }
		 while (k + number_of_new_routes_added < routes.size());

	
	 }while (routes.size() > 0 && monsters_found != monsters.size() && routes.size()<6);
	 
 }

bool contains(vector <Point> point_list, Point point )
{
	for(int i = 0; i < point_list.size(); i++)
	{
		if(point_list.at(i) == point)
			return true;
	}
	return false;
}

int monsterLocatedcheck(const vector<Point> route, vector<Monster> & monsters)
{
	int monsters_found = 0; 

	for(int i = 0; i < monsters.size(); i++)
	{
		if(route.back() == monsters.at(i).location())
		{
			monsters.at(i).setAutoRoute(route);
			monsters_found++;
		}
	}
	return monsters_found;
}

